home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / GameKit / Examples / PacMan / Player.m < prev    next >
Text File  |  1995-06-12  |  7KB  |  216 lines

  1.  
  2. // Handles moving and rendering the Pac, whether under player or demo control.
  3.  
  4. #import <libc.h>
  5. #import "Player.h"
  6. #import "Monster.h"
  7. #import "Maze.h"
  8. #import "PacManView.h"
  9. #import <gamekit/gamekit.h>
  10. #import <appkit/appkit.h>
  11. #import "PacMovement.h"
  12.  
  13. // used to translate PAC_<direction> to px, py velocities
  14. static int pacxvec[9] = { 0, 2, -2, 0,  0, 0, 0, 0,  0 };
  15. static int pacyvec[9] = { 0, 0,  0, 0, -2, 0, 0, 0,  2 };
  16. //static int   opdir[9] = { 0, 2,  1, 0,  8, 0, 0, 0,  4 };
  17. static int bothdir[9] = { 0, 3,  3, 0, 12, 0, 0, 0, 12 };
  18.  
  19. // the y-coord in the Pacs.tiff image indexed by direction
  20. static int pacypic[9] = { 0, 5 * PAC_WIDTH,  4 * PAC_WIDTH, 0,
  21.     6 * PAC_WIDTH, 0, 0, 0, 7 * PAC_WIDTH };
  22. static int pacydie[9] = { 0, 1 * PAC_WIDTH,  0,              0,
  23.     2 * PAC_WIDTH, 0, 0, 0, 3 * PAC_WIDTH };
  24.  
  25.  
  26. @implementation Player
  27.  
  28. - init                        // initialize the player
  29. {
  30.     [super init];
  31.     pacs[1] = [NXImage findImageNamed:"Pacs.tiff"];
  32.     pacs[2] = [NXImage findImageNamed:"PacsBig.tiff"];
  33.     nextDir = PAC_STOP;
  34.     curDir = PAC_STOP;
  35.     playerStopped = NO;
  36.     return self;
  37. }
  38.  
  39. - (BOOL)newPlayer            // get and set up a new Pac.  Returns NO if can't
  40. {
  41.     if (![pacsLeft decNumUp:self]) return NO;        // no pacs left
  42.     [self resetPlayer];
  43.     return YES;
  44. }
  45.  
  46. - resetPlayer                // reset all player info
  47. {
  48.     curDir = PAC_RIGHT;
  49.     nextDir = PAC_RIGHT;
  50.     px = 0; py = 0;
  51.     state = PAC_ALIVE;
  52.     cycle = 0;
  53.     lastx = - 4 * GHOST_SIZE;
  54.     lasty = - 4 * GHOST_SIZE;
  55.     playerStopped = NO;
  56.     [maze playerPosition:&myX :&myY];
  57.     return self;
  58. }
  59.  
  60. - (BOOL)pacAlive                // returns YES if Pac is alive
  61. {
  62.     if (state < PAC_ALIVE) return NO;
  63.     return YES;
  64. }
  65.  
  66. - demoMove:sender    // handles Pac movement during the demo sequence
  67. {
  68.     int i, zz; float xx, yy; int zi = 1;
  69.     int ghost_x = 0;
  70.     int ghost_y = 0;
  71.     int closest = (BLOCK_WIDTH + BLOCK_HEIGHT) * GHOST_SIZE;
  72.     register int tdir = 0x0f;
  73.     register int sense = 0;
  74.     int dx = myX % GHOST_SIZE; int dy = myY % GHOST_SIZE;
  75.     
  76.     // keep us in the track between maze walls
  77.     if (dx) tdir &= 0x03;
  78.     if (dy) tdir &= 0x0c;
  79.     
  80.     for (i=0; i<4; i++) {    // find closest ghost; run away from it.
  81.         [[sender ghost:i] at:&xx :&yy];
  82.         zz = abs(xx - myX) + abs(yy - myY);
  83.         if (zz < closest) {
  84.             zi = i;
  85.             closest = zz;
  86.             ghost_x = xx;
  87.             ghost_y = yy;
  88.     }    }
  89.     
  90. // first, find the directions in which pac can go
  91.     if ((abs(ghost_x - myX) >= GHOST_SIZE * 2 - 2) ||
  92.             (abs(ghost_y - myY) >= GHOST_SIZE * 2 - 2)) {
  93.         if ([maze playerWall:(myX+GHOST_SIZE) :myY] || (px < 0))
  94.             tdir &= ~0x01;
  95.         if ([maze playerWall:(myX-1) :myY] || (px > 0)) tdir &= ~0x02;
  96.         if ([maze playerWall:myX :(myY-1)] || (py > 0)) tdir &= ~0x04;
  97.         if ([maze playerWall:myX :(myY+GHOST_SIZE)] || (py < 0))
  98.             tdir &= ~0x08;
  99.         if ((random() & 0x0f) > 10) {
  100.             // worry about shorter distance first.
  101.             if (abs(ghost_x - myX) < abs(ghost_y - myY)) {
  102.                 if (tdir & 0x03) tdir &= 0x03;
  103.             } else {
  104.                 if (tdir & 0x0c) tdir &= 0x0c;
  105.         }    }
  106.     } else { // if a ghost is close by, we can reverse direction.
  107.         if ([maze playerWall:myX     :(myY-1)]) tdir &= ~0x04;
  108.         if ([maze playerWall:myX :(myY+GHOST_SIZE)]) tdir &= ~0x08;
  109.         if ([maze playerWall:(myX+GHOST_SIZE) :myY]) tdir &= ~0x01;
  110.         if ([maze playerWall:(myX-1) :myY]) tdir &= ~0x02;
  111.     }
  112.     
  113. // now choose the new direction for the pac
  114.     if ([[sender ghost:zi] canBeEaten]) {
  115.     // chase an eatable ghost
  116.         sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1];
  117.         px = chasexvec[tdir][sense];
  118.         py = chaseyvec[tdir][sense];
  119.     } else {
  120.         if (((abs(ghost_x - myX) <= GHOST_SIZE * 2 - 2) &&
  121.                 (abs(ghost_y - myY) <= GHOST_SIZE * 2 - 2)) ||
  122.                 ((random() & 0x0f) > 2)) {    // if close or random allows, go
  123.                 // according to program
  124.             sense = demofind[sgn(ghost_y - myY) + 1][sgn(ghost_x - myX) + 1];
  125.         } else {    // random direction occasionally
  126.              sense = random() & 0x07;
  127.         }
  128.         px = demoxvec[tdir][sense];
  129.         py = demoyvec[tdir][sense];
  130.     }    // set up the direction:
  131.     curDir = demoDir[sgn(py) + 1][sgn(px) + 1];
  132.     return self;
  133. }
  134.  
  135. - move:sender                // Move the PacMan one animation frame
  136. {
  137.     int dx = myX % PAC_WIDTH; int dy = myY % PAC_WIDTH;
  138.     register int tdir = 0x0f;
  139.  
  140.     if (state < PAC_DEAD) { // Pac is dying
  141.         state++;
  142.     } else if (state >= PAC_ALIVE) { // Pac is alive, so move it
  143.  
  144.         if ([gameView demoMode]) {
  145.         // if demo mode, we move the pac ourselves
  146.             [self demoMove:sender];
  147.         } else {
  148.         // if not demo mode, we let the user decide how to move the pac.
  149.             // first, find the directions in which the pac can go
  150.             if (dx || dy) tdir = bothdir[curDir]; // curr. dir or opp.
  151.                 // direction are allowed; other dir. only at junctions.
  152.             if ([maze playerWall:(myX+PAC_WIDTH) :myY]) tdir &= ~PAC_RIGHT;
  153.             if ([maze playerWall:(myX-2) :myY]) tdir &= ~PAC_LEFT;
  154.             if ([maze playerWall:myX     :(myY-2)]) tdir &= ~PAC_DOWN;
  155.             if ([maze playerWall:myX :(myY+PAC_WIDTH)]) tdir &= ~PAC_UP;
  156.  
  157.             // see if we can go the "next" direction the player wants or not...
  158.             if (nextDir & tdir) { // we can!
  159.                 curDir = nextDir;
  160.             }
  161.             if (playerStopped) tdir = 0x00; // don't move if stopped
  162.             // now choose the new direction for the pac
  163.             //   the "&" makes sure we stop if we can't go further in this dir.
  164.             px = pacxvec[curDir & tdir];
  165.             py = pacyvec[curDir & tdir];
  166.         }
  167.  
  168.     } else { // Pac is dead so do nothing.
  169.         px = 0; py = 0;
  170.     }
  171.     return self;
  172. }
  173.  
  174. - newDirection:(int)newDir        // send Pac in new direction.
  175. {
  176.     if (newDir == PAC_STOP) {
  177.         playerStopped = YES;
  178.     } else {
  179.         nextDir = newDir;
  180.         playerStopped = NO;
  181.     }
  182.     return self;
  183. }
  184.  
  185. - pacDie { state = PAC_DYING; return self; }    // the pac will melt
  186.  
  187. - renderAt:(int)posx :(int)posy move:(BOOL)moveOk    // draw pac
  188.         // you should lock focus on view that gets the Pac first.
  189. {
  190.     NXRect from;
  191.     NXPoint pos;
  192.  
  193.     [super renderAt:posx :posy move:moveOk];
  194.     pos.x = myX * scale + posx; pos.y = myY * scale + posy;
  195.  
  196.     if (state == PAC_ALIVE) {    // draw living pac
  197.         NXSetRect(&from, cycle * PAC_WIDTH * scale, pacypic[curDir] * scale,
  198.             PAC_WIDTH * scale, PAC_WIDTH * scale);
  199.     } else if (state < PAC_DEAD) { // draw dying pac
  200.         NXSetRect(&from, (state - PAC_DYING) * PAC_WIDTH * scale,
  201.                 pacydie[curDir] * scale, PAC_WIDTH * scale, PAC_WIDTH * scale);
  202.         if (!cycle) state++;
  203.         if (cycle == 1) cycle = -1; // change period while we die
  204.     } else { // if dead, draw no pac (upper rt. of image is blank)
  205.         NXSetRect(&from, 10 * PAC_WIDTH * scale, 7 * PAC_WIDTH * scale,
  206.             PAC_WIDTH * scale, PAC_WIDTH * scale);
  207.     }
  208.     [pacs[scale] composite:NX_SOVER fromRect:&from toPoint:&pos];
  209.  
  210.     cycle++; if (cycle > 5) cycle = 0;    // cycle through six images.
  211.     return self;
  212. }
  213.  
  214.  
  215. @end
  216.